package com.vcc.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSONObject;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.vcc.core.exception.NotFoundException;
import com.vcc.core.exception.ProgramException;
import com.vcc.core.util.RegexUtil;
import com.vcc.data.entity.Role;
import com.vcc.data.entity.Wallet;
import com.vcc.data.entity.Withdraw;
import com.vcc.data.mapper.WithdrawMapper;
import com.vcc.service.boss.request.withdraw.AuditOrderDto;
import com.vcc.service.sa.BossUserUtil;
import com.vcc.service.service.UserService;
import com.vcc.service.service.WalletDetailService;
import com.vcc.service.service.WalletService;
import com.vcc.service.service.WithdrawService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;

import static com.vcc.data.entity.table.BossUserTableDef.BOSS_USER;
import static com.vcc.data.entity.table.UserTableDef.USER;
import static com.vcc.data.entity.table.WithdrawTableDef.WITHDRAW;

@Service
public class WithdrawServiceImpl extends ServiceImpl<WithdrawMapper, Withdraw> implements WithdrawService {

    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private WalletService walletService;
    @Autowired
    private WalletDetailService walletDetailService;
    @Autowired
    private UserService userService;

    @Override
    public Long getDaySubmitCount(Long userId) {
        DateTime dateTime = new DateTime();
        return count(
                queryChain()
                        .eq(Withdraw::getUserId, userId)
                        .between(Withdraw::getRequestDate, DateUtil.beginOfDay(dateTime), DateUtil.endOfDay(dateTime))
        );
    }

    @Override
    public BigDecimal getDaySumSubmitAmount(Long userId) {
        DateTime dateTime = new DateTime();
//        QueryChain<Withdraw> queryChain = queryChain();
//        queryChain.select(sum(WITHDRAW.AMOUNT));
//        queryChain.where(WITHDRAW.USER_ID.eq(userId));
//        queryChain.where(WITHDRAW.REQUEST_DATE.between(DateUtil.beginOfDay(dateTime), DateUtil.endOfDay(dateTime)));
////        return getObjAs(queryChain, BigDecimal.class);
        BigDecimal decimal = mapper.getDaySumSubmitAmount(userId, DateUtil.beginOfDay(dateTime), DateUtil.endOfDay(dateTime));
        return decimal == null ? BigDecimal.ZERO : decimal.setScale(2, RoundingMode.HALF_UP);
    }

    @Override
    public Page<Withdraw> paging(Page<Withdraw> pageable, Role role, Long userId, Long targetUserId, String type, String startDate, String endDate, String remark, Integer status) {
        QueryChain<Withdraw> queryChain = queryChain();
        if (role.getHasAllData()) {
            queryChain.leftJoin(USER).on(WITHDRAW.USER_ID.eq(USER.ID)).where(
                    USER.PARENT_ID.eq(userId).or(USER.ID.eq(userId))
            );
            if (RegexUtil.checkObjectIsNotNull(targetUserId)) {
                queryChain.eq(Withdraw::getUserId, targetUserId);
            }
        } else {
            queryChain.eq(Withdraw::getUserId, userId);
        }
        if (RegexUtil.checkObjectIsNotNull(type)) {
            queryChain.eq(Withdraw::getType, type);
        }
        if (RegexUtil.checkObjectIsNotNull(remark)) {
            queryChain.eq(Withdraw::getRemark, remark);
        }
        if (RegexUtil.checkObjectIsNotNull(status)) {
            queryChain.eq(Withdraw::getStatus, status);
        }
        if (RegexUtil.checkObjectIsNotNull(startDate)) {
            queryChain.ge(Withdraw::getRequestDate, com.vcc.core.util.DateUtil.getDayBeginDate(startDate, "yyyy-MM-dd"));
        }
        if (RegexUtil.checkObjectIsNotNull(endDate)) {
            queryChain.le(Withdraw::getRequestDate, com.vcc.core.util.DateUtil.getDayEndDate(endDate, "yyyy-MM-dd"));
        }
        queryChain.orderBy(Withdraw::getRequestDate, false);
        return queryChain.page(pageable);
    }

    @Override
    public Page<Withdraw> paging(
            Page<Withdraw> pageable,
            Long targetUserId,
            String type,
            Integer status,
            String startDate,
            String endDate,
            String remark,
            Long dealerId) {
        QueryChain<Withdraw> queryChain = queryChain();
        queryChain.leftJoin(USER).on(WITHDRAW.USER_ID.eq(USER.ID));
        queryChain.leftJoin(BOSS_USER.as("dealer")).on(USER.BE_LONG_TO_DEALER.eq(BOSS_USER.as("dealer").ID));
        queryChain.leftJoin(BOSS_USER.as("audit")).on(WITHDRAW.AUDIT_USER_ID.eq(BOSS_USER.as("audit").ID));
        queryChain.select(WITHDRAW.ALL_COLUMNS);
        queryChain.select(USER.EMAIL.as("account"));
        queryChain.select(BOSS_USER.as("dealer").EMAIL.as("dealerAccount"));
        queryChain.select(BOSS_USER.as("audit").EMAIL.as("auditAccount"));
        if (!BossUserUtil.hasAllData()) {
            Long bossUserId = BossUserUtil.getId();
            queryChain.and(
                    BOSS_USER.as("dealer").ID.eq(bossUserId)
                            .or(BOSS_USER.as("dealer").PARENT_ID.eq(bossUserId))
            );
        } else {
            if (RegexUtil.checkObjectIsNotNull(dealerId)) {
                queryChain.and(
                        BOSS_USER.as("dealer").ID.eq(dealerId)
                                .or(BOSS_USER.as("dealer").PARENT_ID.eq(dealerId))
                );
            }
        }
        if (RegexUtil.checkObjectIsNotNull(targetUserId)) {
            queryChain.eq(Withdraw::getUserId, targetUserId);
        }
        if (RegexUtil.checkObjectIsNotNull(type)) {
            queryChain.eq(Withdraw::getType, type);
        }
        if (RegexUtil.checkObjectIsNotNull(remark)) {
            queryChain.like(Withdraw::getRemark, remark);
        }
        if (RegexUtil.checkObjectIsNotNull(status)) {
            queryChain.eq(Withdraw::getStatus, status);
        }
        if (RegexUtil.checkObjectIsNotNull(startDate)) {
            queryChain.ge(Withdraw::getRequestDate, com.vcc.core.util.DateUtil.getDayBeginDate(startDate, "yyyy-MM-dd"));
        }
        if (RegexUtil.checkObjectIsNotNull(endDate)) {
            queryChain.le(Withdraw::getRequestDate, com.vcc.core.util.DateUtil.getDayEndDate(endDate, "yyyy-MM-dd"));
        }
        queryChain.orderBy(Withdraw::getRequestDate, false);
        return queryChain.page(pageable);
    }

    @Override
    public Boolean audit(AuditOrderDto auditOrderDto) {
        Long loginDealerId = BossUserUtil.getId();
        String key = "withdraw:audit:" + auditOrderDto.getId();
        RLock lock = redissonClient.getLock(key);
        try {
            boolean tryLock = lock.tryLock();
            if (!tryLock) {
                throw new ProgramException("当前订单正在审核中，请稍后再试");
            }
            Withdraw withdraw = queryChain().eq(Withdraw::getId, auditOrderDto.getId()).oneOpt().orElseThrow(() -> new NotFoundException("当前订单无法审核"));
            Long dealerId = userService.getDealerIdById(withdraw.getUserId());
            if (!BossUserUtil.hasAllData() && !loginDealerId.equals(dealerId)) {
                throw new ProgramException("无权操作此订单");
            }
            if (!withdraw.getStatus().equals(2)) {
                throw new ProgramException("当前订单已经审核无法再次审核");
            }
            Wallet userWallet = walletService.getByUserIdAndCurrencyId(
                    withdraw.getUserId(),
                    withdraw.getCurrencyId()
            );
            //提现失败
            if (!auditOrderDto.getStatus()) {
                BigDecimal exchangeRate = userWallet.getWithdrawRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP);
                BigDecimal tempAmount = withdraw.getAmount().multiply(exchangeRate);
                BigDecimal amount = withdraw.getAmount().subtract(tempAmount).setScale(2, RoundingMode.HALF_UP);

                BigDecimal userResultAmount = walletService.addUsableQuotaById(userWallet.getId(), amount, 1, false);
                walletDetailService.accountRecharge(withdraw.getOrderId(), withdraw.getRemark(), amount, userResultAmount, userWallet.getCurrencyId(), withdraw.getUserId());
                updateChain().
                        set(Withdraw::getStatus, 1).
                        set(Withdraw::getAmount, amount).
                        set(Withdraw::getAuditDate, new Date()).
                        set(Withdraw::getAuditUserId, loginDealerId).
                        set(Withdraw::getOrderHash, auditOrderDto.getOrderHash()).
                        set(Withdraw::getRemark, RegexUtil.checkStringIsNull(withdraw.getRemark()) ? "" : withdraw.getRemark() + (RegexUtil.checkStringIsNotNull(auditOrderDto.getRemark()) ? " 审核驳回原因" + auditOrderDto.getRemark() : "审核驳回")).
                        eq(Withdraw::getId, withdraw.getId()).
                        update();
            } else {
                updateChain().
                        set(Withdraw::getStatus, 0).
                        set(Withdraw::getAuditDate, new Date()).
                        set(Withdraw::getAuditUserId, loginDealerId).
                        set(Withdraw::getRemark, RegexUtil.checkStringIsNull(withdraw.getRemark()) ? "" : withdraw.getRemark() + (RegexUtil.checkStringIsNotNull(auditOrderDto.getRemark()) ? " 审核通过原因" + auditOrderDto.getRemark() : "审核通过")).
                        eq(Withdraw::getId, withdraw.getId()).
                        update();
            }
            return true;
        } catch (Exception e) {
            if (e instanceof ProgramException || e instanceof NotFoundException) {
                throw e;
            }
            throw new ProgramException("审核订单失败");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    public List<JSONObject> statistics() {
        //计算昨天的时间 段 00:00:00 - 23:59:59 账户充值 成功的总金额
        QueryChain<Withdraw> queryChain = queryChain().select(
                "user_id as userId",
                "COUNT(*) AS totalCount",
                "SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS successCount",
                "SUM(CASE WHEN status = 0 THEN amount ELSE 0 END) AS successAmount"
        );
        queryChain.where(WITHDRAW.AUDIT_DATE.between(
                com.vcc.core.util.DateUtil.getDayBeginDate(DateTime.now().offset(DateField.DAY_OF_MONTH, -1)),
                com.vcc.core.util.DateUtil.getDayEndDate(DateTime.now().offset(DateField.DAY_OF_MONTH, -1))));
        queryChain.groupBy(WITHDRAW.USER_ID);
        return mapper.selectListByQueryAs(queryChain, Row.class).stream().map(JSONObject::new).toList();
    }

    @Override
    public List<JSONObject> statistics(DateTime startDate, DateTime endDate) {
        //计算昨天的时间 段 00:00:00 - 23:59:59 账户充值 成功的总金额
        QueryChain<Withdraw> queryChain = queryChain().select(
                "user_id as userId",
                "COUNT(*) AS totalCount",
                "SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS successCount",
                "SUM(CASE WHEN status = 0 THEN amount ELSE 0 END) AS successAmount"
        );
        queryChain.where(WITHDRAW.AUDIT_DATE.between(startDate, endDate));
        queryChain.groupBy(WITHDRAW.USER_ID);
        return mapper.selectListByQueryAs(queryChain, Row.class).stream().map(JSONObject::new).toList();
    }

    @Override
    public List<JSONObject> statistics(DateTime startDate, DateTime endDate, Long userId, Long dealerId) {
        //计算昨天的时间 段 00:00:00 - 23:59:59 账户充值 成功的总金额
        QueryChain<Withdraw> queryChain = queryChain().select(
                "DATE_FORMAT(vcc_withdraw.audit_date, '%Y-%m-%d') as date",
                "COUNT(vcc_withdraw.id) AS totalCount",
                "SUM(CASE WHEN vcc_withdraw.status = 0 THEN 1 ELSE 0 END) AS successCount",
                "SUM(CASE WHEN vcc_withdraw.status = 0 THEN amount ELSE 0 END) AS successAmount"
        );
        queryChain.where(WITHDRAW.AUDIT_DATE.between(
                com.vcc.core.util.DateUtil.getDayBeginDate(startDate),
                com.vcc.core.util.DateUtil.getDayEndDate(endDate)));
        queryChain.groupBy("DATE_FORMAT(vcc_withdraw.audit_date, '%Y-%m-%d')");
        if (userId != null) {
            queryChain.and(WITHDRAW.USER_ID.eq(userId));
        }
        if (dealerId != null) {
            queryChain.leftJoin(USER).on(WITHDRAW.USER_ID.eq(USER.ID));
            queryChain.leftJoin(BOSS_USER).on(USER.BE_LONG_TO_DEALER.eq(BOSS_USER.ID));
            queryChain.and(BOSS_USER.ID.eq(dealerId).or(BOSS_USER.PARENT_ID.eq(dealerId)));
        }
        return mapper.selectListByQueryAs(queryChain, Row.class).stream().map(JSONObject::new).toList();
    }
}
